package com.napenalki.toto.service.impl;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import com.napenalki.toto.domain.Event;
import com.napenalki.toto.domain.Match;
import com.napenalki.toto.domain.Prediction;
import com.napenalki.toto.domain.SpecialEvent;
import com.napenalki.toto.domain.SpecialEvents;
import com.napenalki.toto.service.IllegalEntryException;

public class PredictionEvaluator {

    static final String CORRECT_MATCH_RESULT = "CORRECT_MATCH_RESULT";
    static final String CORRECT_MATCH_OUTCOME = "CORRECT_MATCH_OUTCOME";
    static final String CORRECT_GROUP_WINNER = "CORRECT_GROUP_WINNER";
    static final String CORRECT_FINALISTS = "CORRECT_FINALISTS";
    static final String CORRECT_CHAMPION = "CORRECT_CHAMPION";
    static final String CORRECT_BEST_SCORER = "CORRECT_BEST_SCORER";

    private Map<String, Integer> rules = null;

    public PredictionEvaluator(Map<String, Integer> rules) {
        this.rules = rules;
    }

    public int evaluateMatch(Prediction prediction) {

        if (prediction == null || prediction.getEvent() == null) {
            throw new IllegalEntryException(
                    "Prediction and event should not be null");
        }

        Event match = prediction.getEvent();

        if (!(prediction.getEvent() instanceof Match)) {
            return 0; // TODO or throw exception?
        }

        String pred = prediction.getPrediction();
        String result = match.getResult();

        // Either the match is not played yet, or the user has not given a
        // prediction
        if (pred == null || result == null) {
            return 0;
        }

        if (pred.equals(result)) {
            return rules.get(CORRECT_MATCH_RESULT);
        } else if (isCorrectOutcome(pred, result)) {
            return rules.get(CORRECT_MATCH_OUTCOME);
        }

        return 0;
    }

    private boolean isCorrectOutcome(String pred, String result) {
        String[] goalsPred = pred.split(":");
        String[] goalsResult = result.split(":");

        int goalsTeam1Pred = Integer.parseInt(goalsPred[0]);
        int goalsTeam2Pred = Integer.parseInt(goalsPred[1]);
        int goalsTeam1Result = Integer.parseInt(goalsResult[0]);
        int goalsTeam2Result = Integer.parseInt(goalsResult[1]);

        if ((goalsTeam1Pred > goalsTeam2Pred && goalsTeam1Result > goalsTeam2Result)
                || (goalsTeam1Pred == goalsTeam2Pred && goalsTeam1Result == goalsTeam2Result)
                || (goalsTeam1Pred < goalsTeam2Pred && goalsTeam1Result < goalsTeam2Result))
            return true;

        return false;
    }

    public int evaluateSpecialEvent(Prediction specPred) {

        if (specPred == null || specPred.getEvent() == null) {
            throw new IllegalEntryException(
                    "Prediction and event should not be null");
        }

        if (!(specPred.getEvent() instanceof SpecialEvent)) {
            return 0; // TODO or throw exception?
        }

        SpecialEvent predictedEvent = (SpecialEvent) specPred.getEvent();
        SpecialEvents eventType = predictedEvent.getEventType();
        String prediction = specPred.getPrediction();
        String result = predictedEvent.getResult();

        if (eventType != predictedEvent.getEventType()) {
            return 0; // TODO or throw exception?
        }

        // Either the event is not finished yet, or the user has not given a
        // prediction
        if (prediction == null || result == null) {
            return 0;
        }

        // Events with exactly two elements both in the result and in the
        // prediction
        if (eventType == SpecialEvents.FINALISTS) {
            return evaluateFinalists(prediction, result);
        }

        // Events with one element in the prediction and with several elements
        // in the result
        if (predictedEvent.getEventType() == SpecialEvents.BEST_SCORER) {
            return evaluateOneFromList(eventType, prediction, result);
        }

        // Events with exactly one element in the prediction and in the result
        if (prediction.equals(result)) {
            switch (eventType) {
            case GROUP_WINNER_A:
            case GROUP_WINNER_B:
            case GROUP_WINNER_C:
            case GROUP_WINNER_D:
            case GROUP_WINNER_E:
            case GROUP_WINNER_F:
            case GROUP_WINNER_G:
            case GROUP_WINNER_H:
                return rules.get(CORRECT_GROUP_WINNER);
            case CHAMPION:
                return rules.get(CORRECT_CHAMPION);
            }

        }

        return 0;
    }

    private int evaluateOneFromList(SpecialEvents eventType, String prediction,
            String result) {
        List<String> resultList = new LinkedList<String>(Arrays.asList(result
                .split(",")));
        if (resultList.contains(prediction)) {
            switch (eventType) {
            case BEST_SCORER:
                return rules.get(CORRECT_BEST_SCORER);
            }
        }
        return 0;
    }

    private int evaluateFinalists(String prediction, String result) {

        String[] predictedTeams = prediction.split(",");
        String[] actualTeams = result.split(",");

        if (predictedTeams[0].equals(actualTeams[0])
                && predictedTeams[1].equals(actualTeams[1])
                || predictedTeams[1].equals(actualTeams[0])
                && predictedTeams[0].equals(actualTeams[1])) {
            return rules.get(CORRECT_FINALISTS);
        }

        return 0;
    }
}
